package com.wut.utils.tree;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Function;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TreeUtil {

    /**
     * 将 List 转为 Tree
     *
     * @param dataList    节点 List 数据
     * @param getId       节点ID函数接口
     * @param getParentId 父节点ID函数接口
     */
    public static <T, E> List<T> createTree(List<T> dataList, Function<T, E> getId, Function<T, E> getParentId) {
        if (ObjectUtil.isNull(dataList)) {
            return CollUtil.newArrayList();
        }

        Map<E, E> allIdMap = new HashMap<>();
        for (T data : dataList) {
            allIdMap.put(getId.apply(data), getId.apply(data));
        }

        // 父节点列表
        ArrayList<T> parentNodeList = new ArrayList<>();
        // 叶子节点列表
        ArrayList<T> leafNodeList = new ArrayList<>();
        // 找到根节点和非根节点
        if (!dataList.isEmpty()) {
            for (T data : dataList) {
                if (StrUtil.isBlank(String.valueOf(getParentId.apply(data))) || !allIdMap.containsKey(getParentId.apply(data))) {
                    parentNodeList.add(data);
                } else {
                    leafNodeList.add(data);
                }
            }
        }

        try {
            // 递归获取所有子节点
            if (!parentNodeList.isEmpty()) {
                for (T node : parentNodeList) {
                    Method method = node.getClass().getMethod("setChildren", List.class);
                    method.invoke(node, getTreeChild(leafNodeList, getId.apply(node), getId, getParentId));
                }
            }
        } catch (Exception e) {
            log.error("children字段赋值错误:{}", e.getMessage());
        }

        return parentNodeList;
    }

    /**
     * 将 List 转为 Tree并根据关键字过滤
     *
     * @param dataList        节点 List 数据
     * @param getId           节点ID函数接口
     * @param getParentId     父节点ID函数接口
     * @param keyword         关键字
     * @param keywordFunction 关键字字段函数接口集合
     * @return Tree
     */
    public static <T, E> List<T> createTreeByKeyword(List<T> dataList, Function<T, E> getId, Function<T, E> getParentId, String keyword, Function<T, ?>... keywordFunction) {
        if (ObjectUtil.isNull(dataList)) {
            return CollUtil.newArrayList();
        }

        Map<E, T> allDataMap = new HashMap<>();
        for (T data : dataList) {
            allDataMap.put(getId.apply(data), data);
        }

        // 查找 节点的Name 和 关键字Keyword 相关数据
        List<T> treeList = new ArrayList<>();
        if (!dataList.isEmpty()) {
            if (keywordFunction.length > 1) {
                for (T data : dataList) {
                    for (Function<T, ?> function : keywordFunction) {
                        if (String.valueOf(function.apply(data)).toLowerCase().contains(keyword.toLowerCase())) {
                            treeList.add(data);
                        }
                    }
                }
            } else {
                for (T data : dataList) {
                    if (String.valueOf(keywordFunction[0].apply(data)).toLowerCase().contains(keyword.toLowerCase())) {
                        treeList.add(data);
                    }
                }
            }
        }

        // 查询过滤出来的结点和父节点
        treeList = getSelfAndParentNode(treeList, new ArrayList<>(), new HashMap<>(), allDataMap, getId, getParentId);
        // 将过滤出来的数据变成 Tree
        return createTree(treeList, getId, getParentId);
    }

    /**
     * 获取当前节点的父节点列表
     *
     * @param dataList 节点 List 数据
     * @param id       节点ID
     * @return 父节点列表
     */
    public static <T, E> List<T> getParentList(List<T> dataList, Function<T, E> getId, Function<T, E> getParentId, E id) {
        ArrayList<T> parentList = new ArrayList<>();

        while (true) {
            E tempId = id;
            for (T node : dataList) {
                if (ObjectUtil.equals(getId.apply(node), id)) {
                    parentList.add(node);
                    id = getParentId.apply(node);
                    break;
                }
            }
            if (ObjectUtil.equals(tempId, id)) {
                break;
            }
        }
        Collections.reverse(parentList);
        return parentList;
    }

    /**
     * 获取当前节点的子结点列表
     *
     * @param dataList 节点 List 数据
     * @param id       节点ID
     * @return 子结点列表
     */
    public static <T, E> List<T> getChildrenList(List<T> dataList, Function<T, E> getId, Function<T, E> getParentId, E id) {
        List<T> childrenList = new CopyOnWriteArrayList<>();

        for (T node : dataList) {
            if (ObjectUtil.equals(getId.apply(node), id)) {
                childrenList.add(node);
                break;
            }
        }

        int i = 0;

        while (i != childrenList.size()) {
            for (int j = i; j < childrenList.size(); j++) {
                i++;
                for (T node : dataList) {
                    if (ObjectUtil.equals(getParentId.apply(node), getId.apply(childrenList.get(j)))) {
                        childrenList.add(node);
                    }
                }
            }
        }

        return childrenList;
    }

    /**
     * 递归查询子节点
     *
     * @param childList   节点 List 数据
     * @param parentId    父节点ID
     * @param getId       节点ID函数接口
     * @param getParentId 父节点ID函数接口
     */
    private static <T, E> List<T> getTreeChild(List<T> childList, E parentId, Function<T, E> getId, Function<T, E> getParentId)
        throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 父节点列表
        ArrayList<T> parentNodeList = new ArrayList<>();
        // 叶子节点列表
        ArrayList<T> leafNodeList = new ArrayList<>();
        // 找到根节点和非根节点
        if (!childList.isEmpty()) {
            for (T child : childList) {
                if (StrUtil.equals(String.valueOf(getParentId.apply(child)), String.valueOf(parentId))) {
                    parentNodeList.add(child);
                } else {
                    leafNodeList.add(child);
                }
            }
        }

        // 递归获取所有子节点
        if (!parentNodeList.isEmpty()) {
            for (T node : parentNodeList) {
                Method method = node.getClass().getMethod("setChildren", List.class);
                method.invoke(node, getTreeChild(leafNodeList, getId.apply(node), getId, getParentId));
            }
        }
        return parentNodeList;
    }

    /**
     * 递归查询本节点和它们的父节点
     *
     * @param parentList    根据关键字过滤出来的相关节点的父节点
     * @param resultList    过滤出来的节点List
     * @param filterNodeMap 已经过滤出来的节点
     * @param allDataMap    所有节点数据
     */
    private static <T, E> List<T> getSelfAndParentNode(List<T> parentList, List<T> resultList, Map<E, T> filterNodeMap, Map<E, T> allDataMap,
        Function<T, E> getId, Function<T, E> getParentId) {
        if (null == parentList || parentList.isEmpty()) {
            return resultList;
        }

        // 重新创建父节点集合
        List<T> parentNodeList = new ArrayList<>();
        for (T node : parentList) {
            if (!filterNodeMap.containsKey(getId.apply(node))) {
                // 添加到父节点中
                parentNodeList.add(node);
                // 添加到已过滤的map中
                filterNodeMap.put(getId.apply(node), node);
                // 移除集合中相应的元素
                allDataMap.remove(getId.apply(node));
                // 添加到结果集中
                resultList.add(node);
            }

            // 找出本节点的父节点并添加到 parentNodeList 父节点集合中，并移除集合中相应的元素
            if (StrUtil.isNotBlank(String.valueOf(getParentId.apply(node)))) {
                T parentNode = allDataMap.get(getParentId.apply(node));
                if (parentNode != null) {
                    parentNodeList.add(parentNode);
                    allDataMap.remove(getParentId.apply(node));
                }
            }
        }
        // 递归调用
        getSelfAndParentNode(parentNodeList, resultList, filterNodeMap, allDataMap, getId, getParentId);
        return resultList;
    }
}